/*
 * Copyright (c) 2001, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.encoding;

import java.nio.ByteBuffer;

import com.sun.org.omg.SendingContext.CodeBase;

import com.sun.corba.se.pept.encoding.InputObject;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.orb.ORB;

import com.sun.corba.se.spi.transport.CorbaConnection;

import com.sun.corba.se.spi.ior.iiop.GIOPVersion;

import com.sun.corba.se.impl.encoding.BufferManagerFactory;
import com.sun.corba.se.impl.encoding.CodeSetComponentInfo;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.encoding.CDRInputStream;

import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;

import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

import com.sun.corba.se.impl.orbutil.ORBUtility;

/**
 * @author Harold Carr
 */
public class CDRInputObject extends CDRInputStream
    implements
    InputObject {

  private CorbaConnection corbaConnection;
  private Message header;
  private boolean unmarshaledHeader;
  private ORB orb;
  private ORBUtilSystemException wrapper;
  private OMGSystemException omgWrapper;

  public CDRInputObject(ORB orb,
      CorbaConnection corbaConnection,
      ByteBuffer byteBuffer,
      Message header) {
    super(orb, byteBuffer, header.getSize(), header.isLittleEndian(),
        header.getGIOPVersion(), header.getEncodingVersion(),
        BufferManagerFactory.newBufferManagerRead(
            header.getGIOPVersion(),
            header.getEncodingVersion(),
            orb));

    this.corbaConnection = corbaConnection;
    this.orb = orb;
    this.wrapper = ORBUtilSystemException.get(orb,
        CORBALogDomains.RPC_ENCODING);
    this.omgWrapper = OMGSystemException.get(orb,
        CORBALogDomains.RPC_ENCODING);

    if (orb.transportDebugFlag) {
      dprint(".CDRInputObject constructor:");
    }

    getBufferManager().init(header);

    this.header = header;

    unmarshaledHeader = false;

    setIndex(Message.GIOPMessageHeaderLength);

    setBufferLength(header.getSize());
  }

  // REVISIT - think about this some more.
  // This connection normally is accessed from the message mediator.
  // However, giop input needs to get code set info from the connetion
  // *before* the message mediator is available.
  public final CorbaConnection getConnection() {
    return corbaConnection;
  }

  // XREVISIT - Should the header be kept in the stream or the
  // message mediator?  Or should we not have a header and
  // have the information stored in the message mediator
  // directly?
  public Message getMessageHeader() {
    return header;
  }

  /**
   * Unmarshal the extended GIOP header
   * NOTE: May be fragmented, so should not be called by the ReaderThread.
   * See CorbaResponseWaitingRoomImpl.waitForResponse.  It is done
   * there in the client thread.
   */
  public void unmarshalHeader() {
    // Unmarshal the extended GIOP message from the buffer.

    if (!unmarshaledHeader) {
      try {
        if (((ORB) orb()).transportDebugFlag) {
          dprint(".unmarshalHeader->: " + getMessageHeader());
        }
        getMessageHeader().read(this);
        unmarshaledHeader = true;
      } catch (RuntimeException e) {
        if (((ORB) orb()).transportDebugFlag) {
          dprint(".unmarshalHeader: !!ERROR!!: "
              + getMessageHeader()
              + ": " + e);
        }
        throw e;
      } finally {
        if (((ORB) orb()).transportDebugFlag) {
          dprint(".unmarshalHeader<-: " + getMessageHeader());
        }
      }
    }
  }

  public final boolean unmarshaledHeader() {
    return unmarshaledHeader;
  }

  /**
   * Override the default CDR factory behavior to get the
   * negotiated code sets from the connection.
   *
   * These are only called once per message, the first time needed.
   *
   * In the local case, there is no Connection, so use the
   * local code sets.
   */
  protected CodeSetConversion.BTCConverter createCharBTCConverter() {
    CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

    // If the connection doesn't have its negotiated
    // code sets by now, fall back on the defaults defined
    // in CDRInputStream.
    if (codesets == null) {
      return super.createCharBTCConverter();
    }

    OSFCodeSetRegistry.Entry charSet
        = OSFCodeSetRegistry.lookupEntry(codesets.getCharCodeSet());

    if (charSet == null) {
      throw wrapper.unknownCodeset(charSet);
    }

    return CodeSetConversion.impl().getBTCConverter(charSet, isLittleEndian());
  }

  protected CodeSetConversion.BTCConverter createWCharBTCConverter() {

    CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

    // If the connection doesn't have its negotiated
    // code sets by now, we have to throw an exception.
    // See CORBA formal 00-11-03 13.9.2.6.
    if (codesets == null) {
      if (getConnection().isServer()) {
        throw omgWrapper.noClientWcharCodesetCtx();
      } else {
        throw omgWrapper.noServerWcharCodesetCmp();
      }
    }

    OSFCodeSetRegistry.Entry wcharSet
        = OSFCodeSetRegistry.lookupEntry(codesets.getWCharCodeSet());

    if (wcharSet == null) {
      throw wrapper.unknownCodeset(wcharSet);
    }

    // For GIOP 1.2 and UTF-16, use big endian if there is no byte
    // order marker.  (See issue 3405b)
    //
    // For GIOP 1.1 and UTF-16, use the byte order the stream if
    // there isn't (and there shouldn't be) a byte order marker.
    //
    // GIOP 1.0 doesn't have wchars.  If we're talking to a legacy ORB,
    // we do what our old ORBs did.
    if (wcharSet == OSFCodeSetRegistry.UTF_16) {
      if (getGIOPVersion().equals(GIOPVersion.V1_2)) {
        return CodeSetConversion.impl().getBTCConverter(wcharSet, false);
      }
    }

    return CodeSetConversion.impl().getBTCConverter(wcharSet, isLittleEndian());
  }

  // If we're local and don't have a Connection, use the
  // local code sets, otherwise get them from the connection.
  // If the connection doesn't have negotiated code sets
  // yet, then we use ISO8859-1 for char/string and wchar/wstring
  // are illegal.
  private CodeSetComponentInfo.CodeSetContext getCodeSets() {
    if (getConnection() == null) {
      return CodeSetComponentInfo.LOCAL_CODE_SETS;
    } else {
      return getConnection().getCodeSetContext();
    }
  }

  public final CodeBase getCodeBase() {
    if (getConnection() == null) {
      return null;
    } else {
      return getConnection().getCodeBase();
    }
  }

  // -----------------------------------------------------------
  // Below this point are commented out methods with features
  // from the old stream.  We must find ways to address
  // these issues in the future.
  // -----------------------------------------------------------

  // XREVISIT
//     private XIIOPInputStream(XIIOPInputStream stream) {
//         super(stream);

//         this.conn = stream.conn;
//         this.msg = stream.msg;
//         this.unmarshaledHeader = stream.unmarshaledHeader;
//     }

  public CDRInputStream dup() {
    // XREVISIT
    return null;
    // return new XIIOPInputStream(this);
  }

  protected void dprint(String msg) {
    ORBUtility.dprint("CDRInputObject", msg);
  }
}

// End of file.
